package com.yytek.job.service;

import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSONObject;
import com.yytek.common.core.constant.CacheConstants;
import com.yytek.common.core.constant.ProtocolConstans;
import com.yytek.common.core.constant.ScheduleConstants;
import com.yytek.common.core.domain.AttributeInfo;
import com.yytek.common.core.domain.CommandRequest;
import com.yytek.common.core.exception.job.TaskException;
import com.yytek.common.core.utils.DateUtils;
import com.yytek.common.core.utils.bean.BeanCopyUtils;
import com.yytek.common.redis.service.RedisService;
import com.yytek.job.domain.PlanTemplateDeviceTopo;
import com.yytek.job.domain.SysJob;
import com.yytek.job.mapper.PlanTemplateDeviceTopoMapper;
import com.yytek.nmp.api.RemoteNmpService;
import com.yytek.nmp.api.domain.IotDeviceInfo;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yytek.job.mapper.PlanTaskTemplateMapper;
import com.yytek.job.domain.PlanTaskTemplate;
import org.springframework.transaction.annotation.Transactional;

/**
 * 任务模板Service业务层处理
 * 
 * @author yytek-iot
 * @date 2022-07-29
 */
@Service
public class PlanTaskTemplateServiceImpl implements IPlanTaskTemplateService 
{
    @Autowired
    private PlanTaskTemplateMapper planTaskTemplateMapper;
    @Autowired
    private SysJobServiceImpl sysJobService;
    @Autowired
    private RemoteNmpService remoteNmpService;
    @Autowired
    private PlanTemplateDeviceTopoMapper templateDeviceTopoMapper;

    @Autowired
    private RedisService redisService;
    /**
     * 查询任务模板
     * 
     * @param id 任务模板主键
     * @return 任务模板
     */
    @Override
    public PlanTaskTemplate selectPlanTaskTemplateById(Long id)
    {
        return planTaskTemplateMapper.selectPlanTaskTemplateById(id);
    }

    /**
     * 查询任务模板列表
     * 
     * @param planTaskTemplate 任务模板
     * @return 任务模板
     */
    @Override
    public List<PlanTaskTemplate> selectPlanTaskTemplateList(PlanTaskTemplate planTaskTemplate)
    {
        return planTaskTemplateMapper.selectPlanTaskTemplateList(planTaskTemplate);
    }

    /**
     * 新增任务模板
     * 
     * @param planTaskTemplate 任务模板
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertPlanTaskTemplate(PlanTaskTemplate planTaskTemplate) throws Exception {
        planTaskTemplate.setCreateTime(DateUtils.getNowDate());
        planTaskTemplateMapper.insertPlanTaskTemplate(planTaskTemplate);
        //创建任务模板,根据模板类型 创建设备执行任务
        if(planTaskTemplate.getDeviceScope().equals("0")){
            //0-所有对应产品设备
            int rows = createAllDeviceTask(planTaskTemplate);
            return rows;
        }else if(planTaskTemplate.getDeviceScope().equals("1")){
            //1-关联模板设备
            //如果绑定设备列表为空
            if(CollectionUtil.isNotEmpty(planTaskTemplate.getTemplateDeviceTopoList())){
                int rows = createBindDeviceTask(planTaskTemplate);
                return rows;
            }else {
                throw new Exception("绑定设备列表为空");
            }
        }else if(planTaskTemplate.getDeviceScope().equals("2")){
            //2-除关联模板设备外的所有设备
            //如果绑定设备列表为空
            if(CollectionUtil.isNotEmpty(planTaskTemplate.getTemplateDeviceTopoList())){
                int rows = createNoBindDeviceTask(planTaskTemplate);
                return rows;
            }else {
                throw new Exception("绑定设备列表为空");
            }
        }
        return 0;
    }


    /**
     * 创建单个设备的设备任务
     * @param taskTemplate
     */
    public int createNewDeviceTask(PlanTaskTemplate taskTemplate,String deviceId) throws Exception {
        //查询模板绑定设备列表
        PlanTemplateDeviceTopo cxTopo = new PlanTemplateDeviceTopo();
        cxTopo.setTemplateId(taskTemplate.getId());
        List<PlanTemplateDeviceTopo> templateDeviceTopoList = templateDeviceTopoMapper.selectPlanTemplateDeviceTopoList(cxTopo);
        taskTemplate.setTemplateDeviceTopoList(templateDeviceTopoList);
        //根据不同情况生成任务设备
        if(taskTemplate.getDeviceScope().equals("0")){
            //0-所有对应产品设备
            int rows = createOneDeviceTask(taskTemplate,deviceId);
            return rows;
        }else if(taskTemplate.getDeviceScope().equals("1")){
            //1-关联模板设备
            //如果绑定设备列表为空
            if(CollectionUtil.isNotEmpty(taskTemplate.getTemplateDeviceTopoList())){
                int rows = createOneBindDeviceTask(taskTemplate,deviceId);
                return rows;
            }else {
                throw new Exception("绑定设备列表为空");
            }
        }else if(taskTemplate.getDeviceScope().equals("2")){
            //2-除关联模板设备外的所有设备
            //如果绑定设备列表为空
            if(CollectionUtil.isNotEmpty(taskTemplate.getTemplateDeviceTopoList())){
                int rows = createOneNoBindDeviceTask(taskTemplate,deviceId);
                return rows;
            }else {
                throw new Exception("绑定设备列表为空");
            }
        }
        return 0;
    }



    /**
     * 关联模板单独设备的设备
     * @param taskTemplate
     * @param deviceId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createOneBindDeviceTask(PlanTaskTemplate taskTemplate, String deviceId) throws Exception {
        //获取绑定设备关系列表
        List<PlanTemplateDeviceTopo> templateDeviceTopoList = taskTemplate.getTemplateDeviceTopoList();
        //查询到所有设备,新建设备执行任务
        if(CollectionUtil.isNotEmpty(templateDeviceTopoList)){
            for (int i = 0; i < templateDeviceTopoList.size(); i++) {
                PlanTemplateDeviceTopo templateDeviceTopo = templateDeviceTopoList.get(i);
                //如果其中有该设备
                if(templateDeviceTopo.getDeviceId().equals(deviceId)){
                    return createDeviceJobByTemplate(taskTemplate,deviceId);
                }
            }
        }
        return 0;
    }

    /**
     * 关联模板设备的所有设备
     * @param planTaskTemplate
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createBindDeviceTask(PlanTaskTemplate planTaskTemplate) throws Exception {
        //获取绑定设备关系列表
        List<PlanTemplateDeviceTopo> templateDeviceTopoList = planTaskTemplate.getTemplateDeviceTopoList();
        //批量插入的设备关系表
        List<PlanTemplateDeviceTopo> insrtTopoList = new ArrayList<>();
        //查询到所有设备,新建设备执行任务
        List<SysJob> deviceJobs = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(templateDeviceTopoList)){
            for (int i = 0; i < templateDeviceTopoList.size(); i++) {
                PlanTemplateDeviceTopo templateDeviceTopo = templateDeviceTopoList.get(i);
                templateDeviceTopo.setTemplateId(planTaskTemplate.getId());
                SysJob sysJob = createDeviceNewJobByTemplate(planTaskTemplate,templateDeviceTopo.getDeviceId());
                deviceJobs.add(sysJob);
                insrtTopoList.add(templateDeviceTopo);
            }
            //批量新增关系表
            templateDeviceTopoMapper.insertList(insrtTopoList);
            //批量新增任务
            return sysJobService.insertJobList(deviceJobs);
        }else{
            return 0;
        }
    }
    /**
     * 创建单独设备不关联模板的任务
     * @param taskTemplate
     * @param deviceId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createOneNoBindDeviceTask(PlanTaskTemplate taskTemplate, String deviceId) throws Exception {
        //获取绑定设备关系列表
        List<PlanTemplateDeviceTopo> templateDeviceTopoList = taskTemplate.getTemplateDeviceTopoList();
        //获取到绑定的id
        List<String> bindIds = new ArrayList<>();
        templateDeviceTopoList.stream().forEach(it->bindIds.add(it.getDeviceId()));
        //如果关联设备列表不包含该设备id
        if(!bindIds.contains(deviceId)){
            return createDeviceJobByTemplate(taskTemplate,deviceId);
        }
        return 0;
    }
    /**
     * 创建任务模板中未绑定的设备任务
     * @param planTaskTemplate
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createNoBindDeviceTask(PlanTaskTemplate planTaskTemplate) throws Exception {
        //查出项目所有设备
        IotDeviceInfo cxDevcie = new IotDeviceInfo();
        cxDevcie.setProductId(planTaskTemplate.getProductId());
        cxDevcie.setProjectId(planTaskTemplate.getProjectId());
        List<IotDeviceInfo> deviceInfoList = (List<IotDeviceInfo>) remoteNmpService.selectDeviceInfoList(cxDevcie).getData();
        //获取绑定设备关系列表
        List<PlanTemplateDeviceTopo> templateDeviceTopoList = planTaskTemplate.getTemplateDeviceTopoList();

        //获取到绑定的id
        List<String> bindIds = new ArrayList<>();
        templateDeviceTopoList.stream().forEach(it->bindIds.add(it.getDeviceId()));
        //获取到所有设备id
        List<String> deviceIds = new ArrayList<>();
        deviceInfoList.stream().forEach(it->deviceIds.add(it.getIotId()));
        //获取到未绑定设备(不包含绑定设备)
        List<String> collectAddDevcie =
                deviceIds.stream().filter(it -> !bindIds.contains(it)).collect(Collectors.toList());

        //批量插入的设备关系表,放入模板id
        templateDeviceTopoList.stream()
                .forEach(it-> it.setTemplateId(planTaskTemplate.getId()));
        //查询到所有设备,新建设备执行任务
        List<SysJob> deviceJobs = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(collectAddDevcie)){
            for (int i = 0; i < collectAddDevcie.size(); i++) {
                String deviceId = collectAddDevcie.get(i);
                SysJob sysJob = createDeviceNewJobByTemplate(planTaskTemplate,deviceId);
                deviceJobs.add(sysJob);
            }
            //批量新增关系表
            templateDeviceTopoMapper.insertList(templateDeviceTopoList);
            //批量新增任务
            return sysJobService.insertJobList(deviceJobs);
        }else{
            return 0;
        }
    }

    /**
     * 创建单独设备任务
     * @param planTaskTemplate
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createOneDeviceTask(PlanTaskTemplate planTaskTemplate,String deviceId) throws Exception {
        return createDeviceJobByTemplate(planTaskTemplate,deviceId);
    }

    /**
     * 创建插入数据单个设备任务
     * @param planTaskTemplate
     * @param deviceId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createDeviceJobByTemplate(PlanTaskTemplate planTaskTemplate, String deviceId) throws SchedulerException, TaskException {
        SysJob sysJob = createDeviceNewJobByTemplate(planTaskTemplate,deviceId);
        //批量新增任务
        return sysJobService.insertJob(sysJob);
    }

    /**
     * 创建任务模板中所有设备任务
     * @param planTaskTemplate
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int createAllDeviceTask(PlanTaskTemplate planTaskTemplate) throws Exception {
        IotDeviceInfo cxDevcie = new IotDeviceInfo();
        cxDevcie.setProductId(planTaskTemplate.getProductId());
        cxDevcie.setProjectId(planTaskTemplate.getProjectId());
        List<IotDeviceInfo> deviceInfoList = (List<IotDeviceInfo>) remoteNmpService.selectDeviceInfoList(cxDevcie).getData();
        //查询到所有设备,新建设备执行任务
        List<SysJob> deviceJobs = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(deviceInfoList)){
            for (int i = 0; i < deviceInfoList.size(); i++) {
                IotDeviceInfo deviceInfo = deviceInfoList.get(i);
                SysJob sysJob = createDeviceNewJobByTemplate(planTaskTemplate,deviceInfo.getIotId());
                deviceJobs.add(sysJob);
            }
            //批量新增任务
            return sysJobService.insertJobList(deviceJobs);
        }else{
            return 0;
        }
    }

    /**
     * 根据模板和设备id创建任务实体返回
     * @param planTaskTemplate
     * @param iotId
     * @return
     */
    public SysJob createDeviceNewJobByTemplate(PlanTaskTemplate planTaskTemplate, String iotId) {
        //拷贝相同字段数据到任务中
        SysJob sysJob = BeanCopyUtils.copy(planTaskTemplate,SysJob.class);
        //添加任务组名为模板id
        sysJob.setJobGroup(planTaskTemplate.getId().toString());
        //是否并发执行（0允许 1禁止）
        sysJob.setConcurrent("1");
        //设备id
        sysJob.setDeviceId(iotId);
        //关联模板ID
        sysJob.setTemplateId(planTaskTemplate.getId());
        //暂停任务状态
        sysJob.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        //获取设备执行任务方法和字段
        String deviceTaskMethod =getDeviceTaskMethod(planTaskTemplate,iotId);
        sysJob.setInvokeTarget(deviceTaskMethod);
        //新建生成发送信息
        String sendContentJson = getDeviceTaskSendContent(planTaskTemplate,iotId);
        sysJob.setSendContent(sendContentJson);
        return sysJob;
    }

    /**
     * 生成设备任务发送信息
     * @param planTaskTemplate
     * @param iotId
     * @return
     */
    public String getDeviceTaskSendContent(PlanTaskTemplate planTaskTemplate, String iotId) {
        CommandRequest commandRequest = new CommandRequest();
        commandRequest.setDeviceId(iotId);
        //根据协议不同获取设备发送信息
        Map<String, AttributeInfo> dataInfo = getDeviceTaskSendByProtocol(planTaskTemplate);
        commandRequest.setDataInfo(dataInfo);
        return JSONObject.toJSONString(commandRequest);
    }

    /**
     * 根据任务模板协议不同生成不同发送信息
     * @param planTaskTemplate
     * @return
     */
    private Map<String, AttributeInfo> getDeviceTaskSendByProtocol(PlanTaskTemplate planTaskTemplate) {
        Map<String, AttributeInfo> dataInfo = new HashMap<>(16);
        //发送是否任务标识
        AttributeInfo isTaskAtt = new AttributeInfo();
        isTaskAtt.setType("boolean");
        isTaskAtt.setValue(true);
        dataInfo.put("isTask",isTaskAtt);
        //dataInfo发送消息内容
        AttributeInfo dataAtt = new AttributeInfo();
        dataAtt.setType("string");
        dataAtt.setValue(planTaskTemplate.getSendContent());
        dataInfo.put("data",dataAtt);
        //发送协议
        String protocol = planTaskTemplate.getProtocolType().toLowerCase();
        //MQTT协议
        if(protocol.equals(ProtocolConstans.MQTT.toLowerCase())){
            //发送topic
            AttributeInfo topicAtt = new AttributeInfo();
            topicAtt.setValue(planTaskTemplate.getConectContent());
            topicAtt.setType("string");
            dataInfo.put("topic",topicAtt);
        }
        return dataInfo;
    }

    /**
     * 根据模板 获取到设备 执行任务方法 TODO:不是网关走其他方法
     * @param planTaskTemplate
     * @param iotId
     * @return
     */
    public String getDeviceTaskMethod(PlanTaskTemplate planTaskTemplate, String iotId) {
        //判断 采集协议 是否包含 网关协议
        String protocol = planTaskTemplate.getProtocolType().toLowerCase();
        String[] gwArray = ProtocolConstans.GW_ALL_PROTOCOL;
        List<String> resultList = new ArrayList<>(Arrays.asList(gwArray));
        //默认网关协议
        String method = "gwTask.sendMsg('${muId}','${iotId}')";
        //包含网关走网关方法
        if(resultList.contains(protocol)){
            method = method.replace("${muId}",planTaskTemplate.getId().toString());
            method = method.replace("${iotId}",iotId);
        }else{
            //TODO:不包含走其他方法
        }
         return method;
    }

    /**
     * 修改任务模板
     * 
     * @param planTaskTemplate 任务模板
     * @return 结果
     */
    @Override
    public int updatePlanTaskTemplate(PlanTaskTemplate planTaskTemplate)
    {
        planTaskTemplate.setUpdateTime(DateUtils.getNowDate());
        return planTaskTemplateMapper.updatePlanTaskTemplate(planTaskTemplate);
    }

    /**
     * 批量删除任务模板
     * 
     * @param ids 需要删除的任务模板主键
     * @return 结果
     */
    @Override
    public int deletePlanTaskTemplateByIds(Long[] ids)
    {
        return planTaskTemplateMapper.deletePlanTaskTemplateByIds(ids);
    }

    /**
     * 删除任务模板信息
     * 
     * @param id 任务模板主键
     * @return 结果
     */
    @Override
    public int deletePlanTaskTemplateById(Long id)
    {
        return planTaskTemplateMapper.deletePlanTaskTemplateById(id);
    }

    /**
     * 获取任务模板缓存
     * @param id 任务id
     * @return
     */
    public PlanTaskTemplate selectPlanTaskTemplateCacheById(Long id) {
        String key = CacheConstants.TASK_TEMPLATE_INFO + id;
        if(redisService.hasKey(key)){
            return (PlanTaskTemplate)redisService.getCacheObject(key);
        }else{
            PlanTaskTemplate planTaskTemplate = selectPlanTaskTemplateById(id);
            redisService.setCacheObject(key,planTaskTemplate);
            return planTaskTemplate;
        }
    }
}
